package TryCode.TCP;


import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.HashSet;

public class Server {

    // 记录已经打开客户端的 name, 每个name对应一个socket
    public HashMap<String,Socket> name_socket = new HashMap<>();



//    public HashMap<String, String> tmp_store = new HashMap<>();


    // 还没有开启客户端, 但是已经收到消息的 name, 每个name对应一个锁
    public HashMap<String, Object> lock = new HashMap<>();

    private ServerSocket ss;


    public Server() {
        try{
            ss = new ServerSocket(9999);
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    public Server(int port) {
        try{
            ss = new ServerSocket(port);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    // 监听, 得到一个socket
    public Socket listen(){
        Socket res = null;
        try{
            res = this.ss.accept();
        }catch (Exception e){
            e.printStackTrace();
        }
        return res;
    }

    // 读取指定socket的输入, 变成 Message 对象
    public Message receiveMessage(Socket src_socket)   {
        try{
            // 从socket中拿到输入
            InputStream in = src_socket.getInputStream();
            BufferedReader buf = new BufferedReader(new InputStreamReader(in));

            // 还原信息
            String name = buf.readLine();        // 发送者的name
            String other_name = buf.readLine();  // 发送者的目标的name
            String message = buf.readLine();
            return new Message(name, other_name, message);
        } catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    // 给指定socket发信息
    public void sendMessage(Socket target_socket, Message message){
        try{
            OutputStream out = target_socket.getOutputStream();
            out.write(message.toString().getBytes());
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /*
      如果接收方的名字不在注册表里, '
      就储存起来
      再开一个线程, 用来等待
    */
    public void storeMessage(String target_name, Message message){
        new Thread(()->{
            try{
                // 如果target_name还没有对应的锁对象, 就put一个进去
                if(!this.lock.containsKey(target_name))
                    this.lock.put(target_name,new Object());

                // 拿到target_name所对应的锁, 然后阻塞
                Object name_lock = this.lock.get(target_name);
                synchronized (name_lock){
                    name_lock.wait();
                }

                //阻塞完成后说明目标客户打开客户端了, 就把信息发过去
                Socket target_socket = this.name_socket.get(target_name);

                sendMessage(target_socket, message);

            }catch (Exception e){
                e.printStackTrace();
            }

        }).start();

    }


    public static void main(String[] args) {

        Server server = new Server();

        while(true){
            // 监听得到一个socket
            Socket socket = server.listen();

            // 对每一个socket, 开启一个线程进行处理
            new Thread(()->{
                while(!socket.isClosed()) {
                    try {
                        // 拿出发来的信息
                        Message message = server.receiveMessage(socket);

                        System.out.println("server get message:\n" + message);

                        String name = message.getSender_name();
                        String other_name = message.getReceiver_name();
                        String content = message.getContent();

                        System.out.println(message); //test

                        // 客户端第一次发来通知说自己连接上了
                        if (name.equals(other_name)) {
                            if (content.equals("run")) {
                                server.name_socket.put(name, socket);
                                // notify给lock里面的锁,这样就能收到阻塞的消息了
                                if (server.lock.containsKey(name)) {
                                    synchronized (server.lock.get(name)) {
                                        server.lock.get(name).notifyAll();
                                    }
                                }
                            } else if (content.equals("exit")) {
                                server.name_socket.remove(name);
                            }
                        } else {
                            // 看一看发送方有没有进入注册表
                            if (server.name_socket.containsKey(name)) {
                                // 已经在了, 什么都不用做了
                            } else {
                                // 如果发送者还没有开启客户端,
                                // 先记录一下, 再开个线程进行等待
                                server.name_socket.put(name, socket);
                            }

                            // 看看目标有没有开启客户端
                            if (server.name_socket.containsKey(other_name)) {
                                // 目标已经开启了socket, 可以直接发
                                server.sendMessage(socket, message);
                                System.out.println("直接发:" + message);
                                //                            System.out.println(message); //test
                            } else {
                                // 目标还没开启客户端, 需要服务器暂存
                                server.storeMessage(other_name, message);
                            }
                        }
                    } catch (Exception e) {
                        break;
//                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}
